use std::str::FromStr;

use actix_http::Method;
use actix_web::{
    web::{self, Json, ServiceConfig},
    HttpResponse,
};
use wither::bson::{doc, oid::ObjectId};

use crate::{
    errors::cons_error,
    middlewares::authorize::{club_getters::default_getter, Authorize},
    models::{
        club::ClubModelDTO,
        club_activity::{ClubActivityJoinModelDTO, ClubActivityModelDTO},
        club_form::ClubFormModelDTO,
        invite::InviteTokenType,
        like_list::LikeListModelDTO,
        user::{NormalUserModel, UserModelDTO, UserRole},
    },
    services::{
        club_service::{
            ClubMemberDTO, ClubService, CreateClubActivitySvrReq, CreateClubReq, FindAllClubsReq,
            UpdateClubActivitySvrReq,
        },
        form_service::FormService,
        invite_service::InviteService,
    },
    utils::authorize::check_user_has_permission_with_object_id,
    AppError, AppResult, PaginationQuery,
};

use super::OK_RESPONSE;
mod ctrl_struct {
    use serde::{Deserialize, Serialize};
    use serde_json::Value;
    use std::str::FromStr;
    use wither::bson::{oid::ObjectId, DateTime};

    use crate::{
        models::{
            club_activity::{ClubActivityJoinedUser, ClubActivityLocation},
            page::PageModel,
        },
        services::{
            club_service::{CreateClubActivitySvrReq, UpdateClubActivitySvrReq, UpdateClubReq},
            form_service::JoinClubActivitySvrReq,
        },
        utils::db_utils::UpdateField,
        AppError, AppResult,
    };
    define_request_struct!(AddUserToClubReq {
        club_id: String,
        user_id: String
    });
    define_filter_request_struct!(SaerchAllClubsReq {});
    define_request_struct!(UpdateClubCtrlReq {
        club_id:String,
        name: Option<String>,
        primary_color: Option<String>,
        page: Option<PageModel>,
        location: Option<String>,
        address: Option<String>,
        introduction: Option<String>,
        favicon: Option<String>,
        inner_visible: Option<bool>,
        outer_visible: Option<bool>,
        cover_images: Option<Vec<String>>
    });
    impl From<UpdateClubCtrlReq> for UpdateClubReq {
        fn from(req: UpdateClubCtrlReq) -> Self {
            Self {
                name: req.name,
                primary_color: req.primary_color,
                page: req.page,
                location: req.location,
                address: req.address,
                introduction: req.introduction,
                favicon: req.favicon,
                inner_visible: req.inner_visible,
                outer_visible: req.outer_visible,
                cover_images: req.cover_images,
            }
        }
    }
    define_request_struct!(DeleteClubReq { club_id: String });
    define_filter_request_struct!(SearchAllMembersReq { club_id: String });
    define_request_struct!(JoinClubReq { club_id: String });
    define_filter_request_struct!(SearchFormInClubReq { club_id: String });
    define_request_struct!(ClubDetailReq { club_id: String });
    define_request_struct!(ClubLikeReq { club_id: String });
    define_filter_request_struct!(SearchClubActivityReq {
        club_id:Option<String>
    });
    define_request_struct!(CreateClubActivityReq{
        club_id:String,
        form_id:Option<String>,
        name:String,
        description:Option<String>,
        page:Option<PageModel>,
        location:ClubActivityLocation,
        beginning_time:chrono::DateTime<chrono::Utc>,
        ended_time:Option<chrono::DateTime<chrono::Utc>>
    });

    impl TryFrom<CreateClubActivityReq> for CreateClubActivitySvrReq {
        type Error = AppError;
        fn try_from(value: CreateClubActivityReq) -> Result<Self, Self::Error> {
            let club_id = ObjectId::from_str(&value.club_id)?;
            let form_id = match value.form_id {
                None => None,
                Some(value) => Some(ObjectId::from_str(&value)?),
            };
            Ok(Self {
                club_id,
                form_id,
                name: value.name,
                description: value.description,
                page: value.page,
                location: value.location,
                beginning_time: DateTime::from_chrono(value.beginning_time),
                ended_time: value.ended_time.map(DateTime::from_chrono),
            })
        }
    }

    define_request_struct!(UpdateClubActivityReq {
        activity_id:String,
        #[serde(default="Default::default")]
        form_id:UpdateField<String>,
        #[serde(default="Default::default")]
        name:Option<String>,
        #[serde(default="Default::default")]
        description:UpdateField<String>,
        #[serde(default="Default::default")]
        page:UpdateField<PageModel>
    });

    impl TryFrom<UpdateClubActivityReq> for UpdateClubActivitySvrReq {
        type Error = AppError;
        fn try_from(value: UpdateClubActivityReq) -> Result<Self, Self::Error> {
            let activity_id = ObjectId::from_str(&value.activity_id)?;
            let form_id = match value.form_id {
                UpdateField::Unset => UpdateField::Unset,
                UpdateField::Unchanged => UpdateField::Unchanged,
                UpdateField::Set(v) => UpdateField::Set(ObjectId::from_str(&v)?),
            };
            Ok(Self {
                activity_id,
                form_id,
                name: value.name,
                description: value.description,
                page: value.page,
            })
        }
    }

    define_request_struct!(DeleteClubActivityReq {
        activity_id: String
    });
    define_request_struct!(JoinClubActivityReq {
        activity_id:String,
        content:Option<Value>
    });

    impl JoinClubActivityReq {
        pub fn try_to_svr_req<T: Into<ClubActivityJoinedUser>>(
            self,
            user: T,
        ) -> AppResult<JoinClubActivitySvrReq> {
            let activity_id = ObjectId::from_str(&self.activity_id)?;
            Ok(JoinClubActivitySvrReq {
                activity_id,
                content: self.content,
                joined_user: user.into(),
            })
        }
    }

    define_request_struct!(CheckClubActivityAlreadyJoinedReq {
        activity_id: String
    });

    define_request_struct!(LeaveClubActivityReq {
        activity_id: String
    });

    define_filter_request_struct!(SearchActivityJoinedReq {
        activity_id: String
    });

    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct CheckClubActivityAlreadyJoinedRes {
        pub joined: bool,
    }

    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct StatusRes {
        pub success: bool,
    }

    define_request_struct!(WatchClubRecordReq { club_id: String });
}

async fn search_all_clubs(
    club_service: web::Data<ClubService>,
    page: web::Query<PaginationQuery>,
    search: web::Json<ctrl_struct::SaerchAllClubsReq>,
) -> AppResult<HttpResponse> {
    let res = club_service
        .find_all_clubs(&FindAllClubsReq {
            filter: search
                .into_inner()
                .filter
                .unwrap_or_else(|| serde_json::json!({})),
            page: page.into_inner(),
        })
        .await?;
    Ok(HttpResponse::Ok().json(res.map_into::<ClubModelDTO>()))
}

async fn update_club(
    club_service: web::Data<ClubService>,
    req: Json<ctrl_struct::UpdateClubCtrlReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let oid = ObjectId::from_str(&req.club_id)?;
    if !check_user_has_permission_with_object_id(user, oid, UserRole::LeaderOrAdmin) {
        return Err(AppError::ConsError(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    Ok(HttpResponse::Ok().json(ClubModelDTO::from(
        club_service
            .update_club(oid, &req.into_inner().into())
            .await?,
    )))
}

async fn delete_club(
    club_service: web::Data<ClubService>,
    req: web::Json<ctrl_struct::DeleteClubReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let oid = ObjectId::from_str(&req.club_id)?;
    if !check_user_has_permission_with_object_id(user, oid, UserRole::Admin) {
        return Err(AppError::ConsError(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    club_service.delete_club(oid).await?;
    Ok(HttpResponse::Ok().json(OK_RESPONSE))
}

async fn create_club(
    club_service: web::Data<ClubService>,
    req: web::Json<CreateClubReq>,
) -> AppResult<HttpResponse> {
    let res = club_service.create_club(req.into_inner()).await?;
    Ok(HttpResponse::Ok().json(ClubModelDTO::from(res)))
}

async fn search_all_members(
    club_service: web::Data<ClubService>,
    where_value: web::Json<ctrl_struct::SearchAllMembersReq>,
    query: web::Query<PaginationQuery>,
) -> AppResult<HttpResponse> {
    let club_id = ObjectId::from_str(&where_value.club_id)?;
    let filter = where_value
        .get_filter_as_document_use_default()
        .unwrap_or_default();
    let res = club_service
        .find_all_member_in_club(club_id, filter, &query)
        .await?;
    Ok(HttpResponse::Ok().json(res.map_into::<ClubMemberDTO>()))
}

/// ## join_club
/// 用户主动加入社团，前提是用户使用过invite_token并且此token的type为Immediate
async fn join_club(
    invite_service: web::Data<InviteService>,
    user: NormalUserModel,
    req: web::Json<ctrl_struct::JoinClubReq>,
) -> AppResult<HttpResponse> {
    let club_id = ObjectId::from_str(&req.club_id)?;
    let in_token = invite_service
        .check_user_in_club_invite_token(
            club_id,
            user.id,
            doc! {
                "type":InviteTokenType::Immediate
            },
        )
        .await?;
    if !in_token {
        return Err(AppError::ConsError(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    let res = invite_service
        .invite_user_to_club(user.clone(), user.id, club_id)
        .await?;

    Ok(HttpResponse::Ok().json(UserModelDTO::from(res)))
}

async fn add_to_club(
    invite_service: web::Data<InviteService>,
    user: NormalUserModel,
    req: Json<ctrl_struct::AddUserToClubReq>,
) -> AppResult<HttpResponse> {
    let club_id = ObjectId::from_str(&req.club_id)?;
    let user_id = ObjectId::from_str(&req.user_id)?;
    let res = invite_service
        .invite_user_to_club(user, user_id, club_id)
        .await?;
    Ok(HttpResponse::Ok().json(UserModelDTO::from(res)))
}

async fn search_form_in_club(
    form_service: web::Data<FormService>,
    query: web::Query<PaginationQuery>,
    where_value: web::Json<ctrl_struct::SearchFormInClubReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let club_id = ObjectId::from_str(&where_value.club_id)?;
    let mut filter = where_value
        .get_filter_as_document_use_default()
        .unwrap_or_default();
    filter.insert("club_id", &club_id);
    let res = {
        if check_user_has_permission_with_object_id(user, club_id, UserRole::LeaderOrAdmin) {
            form_service.find_all_forms(&query, filter).await?
        } else {
            form_service.find_all_forms_outer(&query, filter).await?
        }
    };
    Ok(HttpResponse::Ok().json(res.map_into::<ClubFormModelDTO>()))
}

async fn club_detail(
    club_service: web::Data<ClubService>,
    req: web::Json<ctrl_struct::ClubDetailReq>,
) -> AppResult<HttpResponse> {
    let club_id = ObjectId::from_str(&req.club_id)?;
    let res = club_service.club_detail(club_id).await?;
    Ok(HttpResponse::Ok().json(ClubModelDTO::from(res)))
}
async fn like_club(
    svr: web::Data<ClubService>,
    req: web::Json<ctrl_struct::ClubLikeReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let club_id = ObjectId::from_str(&req.club_id)?;
    let res = svr.user_like_club(user.id, club_id).await?;
    Ok(HttpResponse::Ok().json(LikeListModelDTO::from(res)))
}

async fn search_club_activities(
    svr: web::Data<ClubService>,
    req: web::Json<ctrl_struct::SearchClubActivityReq>,
    query: web::Query<PaginationQuery>,
) -> AppResult<HttpResponse> {
    let club_id = match req.club_id {
        Some(ref v) => Some(ObjectId::from_str(v)?),
        None => None,
    };
    let res = svr
        .search_club_activitys(
            club_id,
            req.get_filter_as_document_use_default(),
            &query.into_inner(),
        )
        .await?;
    Ok(HttpResponse::Ok().json(res.map_into::<ClubActivityModelDTO>()))
}
/// ## 创建社团活动
async fn create_club_activity(
    svr: web::Data<ClubService>,
    req: web::Json<ctrl_struct::CreateClubActivityReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let req = CreateClubActivitySvrReq::try_from(req.into_inner())?;
    if !check_user_has_permission_with_object_id(user, req.club_id, UserRole::LeaderOrAdmin) {
        return Err(AppError::no_permission(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    let res = svr.create_club_activity(req).await?;
    Ok(HttpResponse::Ok().json(ClubActivityModelDTO::from(res)))
}

/// ## 更新社团活动
async fn update_club_activity(
    svr: web::Data<ClubService>,
    req: web::Json<ctrl_struct::UpdateClubActivityReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let req = UpdateClubActivitySvrReq::try_from(req.into_inner())?;
    let activity = svr
        .find_one_club_activity(req.activity_id, None)
        .await?
        .ok_or(AppError::ConsError(
            cons_error::MSG_CLUB_ACTIVITY_IS_NOT_FOUND,
        ))?;
    if !check_user_has_permission_with_object_id(user, activity.club_id, UserRole::LeaderOrAdmin) {
        return Err(AppError::no_permission(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    let res = svr.update_club_activity(req).await?;
    Ok(HttpResponse::Ok().json(ClubActivityModelDTO::from(res)))
}
/// ## 删除社团活动
async fn delete_club_activity(
    svr: web::Data<ClubService>,
    req: web::Json<ctrl_struct::DeleteClubActivityReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let activity_id = ObjectId::from_str(&req.activity_id)?;
    let activity =
        svr.find_one_club_activity(activity_id, None)
            .await?
            .ok_or(AppError::ConsError(
                cons_error::MSG_CLUB_ACTIVITY_IS_NOT_FOUND,
            ))?;
    if !check_user_has_permission_with_object_id(user, activity.club_id, UserRole::LeaderOrAdmin) {
        return Err(AppError::no_permission(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    let res = svr.delete_club_activity(activity_id).await?;
    Ok(HttpResponse::Ok().json(res))
}

async fn join_club_activity(
    svr: web::Data<FormService>,
    req: web::Json<ctrl_struct::JoinClubActivityReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let req = req.into_inner();
    let svr_req = req.try_to_svr_req(user)?;
    let res = svr.join_club_activity(svr_req).await?;
    Ok(HttpResponse::Ok().json(ClubActivityJoinModelDTO::from(res)))
}
async fn check_user_already_joined_club_activity(
    svr: web::Data<FormService>,
    user: NormalUserModel,
    req: web::Json<ctrl_struct::CheckClubActivityAlreadyJoinedReq>,
) -> AppResult<HttpResponse> {
    let activity_id = ObjectId::from_str(&req.activity_id)?;
    let res = svr
        .check_club_activity_already_joined(activity_id, user.id)
        .await?;

    Ok(HttpResponse::Ok().json(ctrl_struct::CheckClubActivityAlreadyJoinedRes { joined: res }))
}

async fn leave_club_activity(
    svr: web::Data<FormService>,
    user: NormalUserModel,
    req: web::Json<ctrl_struct::LeaveClubActivityReq>,
) -> AppResult<HttpResponse> {
    let activity_id = ObjectId::from_str(&req.activity_id)?;
    let res = svr.leave_club_activity(activity_id, user.id).await?;
    Ok(HttpResponse::Ok().json(ctrl_struct::StatusRes { success: res }))
}

async fn search_activity_joined(
    svr: web::Data<FormService>,
    req: web::Json<ctrl_struct::SearchActivityJoinedReq>,
    query: web::Query<PaginationQuery>,
) -> AppResult<HttpResponse> {
    let activity_id = ObjectId::from_str(&req.activity_id)?;
    let res = svr
        .search_activity_already_joined(
            activity_id,
            req.get_filter_as_document_use_default(),
            query.into_inner(),
        )
        .await?;
    Ok(HttpResponse::Ok().json(res.map_into::<ClubActivityJoinModelDTO>()))
}

async fn watch_club_record(
    svr: web::Data<ClubService>,
    req: web::Json<ctrl_struct::WatchClubRecordReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let club_id = ObjectId::from_str(&req.club_id)?;
    let res = svr.watch_club_record(club_id, user).await?;
    Ok(HttpResponse::Ok().json(ClubModelDTO::from(res)))
}
async fn get_detail_and_watch(
    svr: web::Data<ClubService>,
    req: web::Json<ctrl_struct::ClubDetailReq>,
    user: Option<NormalUserModel>,
) -> AppResult<HttpResponse> {
    let club_id = ObjectId::from_str(&req.club_id)?;
    if let Some(user) = user {
        let (club_detail, _) = futures::future::join(
            svr.club_detail(club_id),
            svr.watch_club_record(club_id, user),
        )
        .await;
        return Ok(HttpResponse::Ok().json(ClubModelDTO::from(club_detail?)));
    }
    let club_detail = svr.club_detail(club_id).await?;
    Ok(HttpResponse::Ok().json(ClubModelDTO::from(club_detail)))
}

pub fn configure_club_controller(config: &mut ServiceConfig) {
    config
        .service(
            web::resource("/create")
                .route(web::post().to(create_club))
                .wrap(Authorize::new(UserRole::Admin, default_getter).method(Method::POST)),
        )
        .route("/search", web::post().to(search_all_clubs))
        .route("/update", web::post().to(update_club))
        .route("/delete", web::post().to(delete_club))
        .route("/detail", web::post().to(club_detail))
        .route("/detail_watch", web::post().to(get_detail_and_watch))
        .route("/watch", web::post().to(watch_club_record))
        .service(web::scope("/members").route("/search", web::post().to(search_all_members)))
        .route("/join", web::post().to(join_club))
        .service(
            web::resource("/add")
                .route(web::post().to(add_to_club))
                .wrap(Authorize::new(UserRole::Admin, default_getter).method(Method::POST)),
        )
        .service(web::scope("/form").route("/search", web::post().to(search_form_in_club)))
        .service(web::scope("/like").route("", web::patch().to(like_club)))
        .service(
            web::scope("/activity")
                .route("/search", web::post().to(search_club_activities))
                .route("/create", web::post().to(create_club_activity))
                .route("/update", web::post().to(update_club_activity))
                .route("/delete", web::post().to(delete_club_activity))
                .route("/join", web::post().to(join_club_activity))
                .route(
                    "/exist",
                    web::post().to(check_user_already_joined_club_activity),
                )
                .route("/leave", web::post().to(leave_club_activity))
                .route("/search_joined", web::post().to(search_activity_joined)),
        );
}
